package alipay.openplatform.edu.campus.jiuye.service.services.activity;

import alipay.openplatform.edu.campus.jiuye.common.cache.CacheManagerService;
import alipay.openplatform.edu.campus.jiuye.common.enums.DateEnum;
import alipay.openplatform.edu.campus.jiuye.common.page.Page;
import alipay.openplatform.edu.campus.jiuye.common.utils.CopyVoPo;
import alipay.openplatform.edu.campus.jiuye.common.utils.DateUtil;
import alipay.openplatform.edu.campus.jiuye.common.utils.ObjectUtils;
import alipay.openplatform.edu.campus.jiuye.dao.mappers.activity.BllGoodVoiceNoteMapper;
import alipay.openplatform.edu.campus.jiuye.dao.mappers.activity.BllGoodVoiceUserMapper;
import alipay.openplatform.edu.campus.jiuye.dao.mappers.ativitytimes.BllActivityTimesMapper;
import alipay.openplatform.edu.campus.jiuye.model.po.BllActivityTimesPO;
import alipay.openplatform.edu.campus.jiuye.model.po.BllGoodVoicePO;
import alipay.openplatform.edu.campus.jiuye.model.vo.BllGoodVoiceVO;
import alipay.openplatform.edu.campus.jiuye.service.dao.ApiResult;
import alipay.openplatform.edu.campus.jiuye.service.iservice.activity.IBllGoodVoiceUserService;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by wangdan on 16/10/27.
 * 好声音活动
 */
@Service
public class BllGoodVoiceUserService implements IBllGoodVoiceUserService {


    private static final Logger logger = LoggerFactory.getLogger(BllGoodVoiceUserService.class);

    @Autowired
    private BllGoodVoiceUserMapper bllGoodVoiceUserMapper;

    @Autowired
    private CacheManagerService cacheManagerService;

    @Autowired
    private BllGoodVoiceNoteMapper bllGoodVoiceNoteMapper;


    final static String ACTIVITYTIMES = "ACTIVITYTIMES";

    @Autowired
    private BllActivityTimesMapper bllActivityTimesMapper;


    /**
     * 投票
     *
     * @return 当前用户的投票数量
     * @id 用户的ID
     */
    public ApiResult sendVote(Integer id, String uid) {


        boolean isRun = isRunNext();
        if (!isRun) {
            return ApiResult.buildSuccess("-1", "网络环境不好,投票失败!", null);
        }

        Map<String, Object> result = new HashMap<String, Object>();
        result = sendVoteCache(id, uid);

        int code = Integer.parseInt(result.get("code").toString());

        //result.put("data", count);
        if (code == 0) {
            return ApiResult.buildSuccess("0", "投票成功!", result);
        }
        if (code == -2) {
            return ApiResult.buildSuccess("-2", "您今天已经给本选手投过,不能重复投!", result);
        }
        if (code == -3) {
            return ApiResult.buildSuccess("-3", "您今天已经投票超过5位,不能再投!", result);
        }
        return null;
    }


    /**
     * 获取当前用户今天的投票记录
     *
     * @uid 登陆用户的编号
     */
    public ApiResult getUserSendInfoNow(String uid) {
        Date date = new Date();
        DateFormat df = DateFormat.getDateInstance();//日期格式，精确到日
        String nowString = df.format(date);
        String touKey = "uid_" + uid + "_date_" + nowString;

        Map<String, Object> resMap = new HashMap<String, Object>();

        try {


            Set<String> sets = cacheManagerService.smembers(touKey);
            int count = (int) cacheManagerService.scard(touKey);

            resMap.put("code", 0);        //错误码
            resMap.put("usersID", sets);     //当前投的列表
            resMap.put("dateCount", count);     //当前日期,本用户总计投了几个人
        } catch (Exception e) {
            resMap.put("code", -1);        //错误码
            resMap.put("usersID", null);     //当前投的列表
            resMap.put("dateCount", 0);     //当前日期,本用户总计投了几个人
        }

        int code = Integer.parseInt(resMap.get("code").toString());

        //result.put("data", count);
        if (code == 0) {
            return ApiResult.buildSuccess("0", "投票成功!", resMap);
        } else {
            return ApiResult.buildSuccess("0", "投票异常,请重新投票!", resMap);
        }
    }

    /**
     * 设置选手列表中哪些是当前用户投过的
     *
     * @id 当前选手的ID
     * @uid 登陆用户的编号
     */
    public List<BllGoodVoiceVO> setUserSendState(List<BllGoodVoiceVO> list, String uid) throws ParseException {
        boolean flag = ifBeforeDate(DateEnum.DEADLINE.name);
        for (BllGoodVoiceVO bllGoodVoiceVO : list) {

            boolean isSend = isSendForUser(bllGoodVoiceVO.getId(), uid);
            bllGoodVoiceVO.setIfVote(isSend);
            if (!flag) {
                bllGoodVoiceVO.setIfVote(true);
            }
        }

        return list;
    }

    /**
     * 当前用户今天投的次数
     *
     * @id 当前选手的ID
     * @uid 登陆用户的编号
     */
    public int sendCount(String uid) {
        Date date = new Date();
        DateFormat df = DateFormat.getDateInstance();//日期格式，精确到日
        String nowString = df.format(date);
        //当前用户的投票列表
        String touKey = "uid_" + uid + "_date_" + nowString;
        int count = (int) cacheManagerService.scard(touKey);
        return count;
    }


    /**
     * 当天当前用户是否给制定用户投过票
     *
     * @id 当前选手的ID
     * @uid 登陆用户的编号
     */
    public boolean isSendForUser(Integer id, String uid) {
        Date date = new Date();
        DateFormat df = DateFormat.getDateInstance();//日期格式，精确到日
        String nowString = df.format(date);
        //当前用户的投票列表
        String touKey = "uid_" + uid + "_date_" + nowString;

        return cacheManagerService.sismember(touKey, id.toString());
    }

    /**
     * 投票-缓存
     *
     * @id 当前选手的ID
     * @uid 登陆用户的编号
     */
    public  Map sendVoteCache(Integer id,String uid)
    {

        Date date = new Date();
        DateFormat df = DateFormat.getDateInstance();//日期格式，精确到日
        String nowString = df.format(date);
        //当前用户的投票列表
        String touKey = "uid_" + uid + "_date_" + nowString;

        //错误码
        int code = 0;

        boolean hasTou = cacheManagerService.exists(touKey);
        //投的总人数
        Integer count = 0;
        //是否给当前选手投过
        boolean bl = false;

        if (hasTou) {
            //count = Integer.parseInt(cacheManagerService.get(countKey));
            count = (int) cacheManagerService.scard(touKey);
            bl = cacheManagerService.sismember(touKey, id.toString());
        }

        String key = "stu_" + id.toString();
        //如果没有给当前人投,并且本用户投的总次数小于5,那么可以投
        Map<String, Object> resMap = new HashMap<String, Object>();
        //投票的总数量
        long result = 0;
        boolean nowIsTou = false;
        if (!bl && count < 5) {
            nowIsTou = true;
            count = count + 1;
            result = cacheManagerService.hincrby(key, "count", 1);
            String startTime = cacheManagerService.hget(key, "createtime");

            cacheManagerService.sadd(touKey,id.toString());
            cacheManagerService.expire(touKey,86400);

            try {
                DateFormat df2 = DateFormat.getDateTimeInstance();
                int miao = calLastedTime(df2.parse(startTime), date);

                for(int i = 0; i < 6; i++)
                {
                    String areaKey = "area_" + i + "";
                    boolean isNode = cacheManagerService.exists(areaKey);
                    long loo = cacheManagerService.zrevrank(areaKey,key);
                    if(isNode && loo >= 0)
                    {
                        cacheManagerService.zincrby(areaKey,1,key);
                    }
                }
                //大于5分钟将刷新到数据库中,同时更新缓存的时间戳
                if (miao > 300) {
                    synchronized (this) {
                        startTime = cacheManagerService.hget(key, "createtime");
                        miao = calLastedTime(df2.parse(startTime), date);
                        if (miao > 300) {

                            cacheManagerService.hset(key, "createtime", df2.format(date));
                            int up = bllGoodVoiceUserMapper.updateUserCount(id, (int) result);
                        }

                    }
                }
            } catch (Exception e) {
                String err = e.getMessage();
            }

        } else {
            result = Integer.parseInt(cacheManagerService.hget(key, "count"));
        }

        //给本选手投过
        if (bl) {
            code = -2;
        }
        //已经投过5人
        if (!nowIsTou && count >= 5) {
            code = -3;
        }

        //本用户今天投的列表
        //Set<String> sets = cacheManagerService.smembers(touKey);
        //当前日期,本用户总计投了几个人
        //count = (int)cacheManagerService.scard(touKey);

        resMap.put("count", result);     //当前选手被人投票的数量.
        resMap.put("code", code);        //错误码
        //resMap.put("usersID",sets);     //本用户今天投的列表
        resMap.put("dateCount", count);     //当前日期,本用户总计投了几个人

        return resMap;
    }

    /**
     * 设置缓存的信息
     *
     * @list 选手信息列表
     * @areaId 地区编号
     */
    public boolean setListCache(List<BllGoodVoiceVO> list, int areaId) {
        if (list == null || list.size() <= 0) return false;

        try {

            //将所有选手的信息加入缓存中,一个人是一个HASH
            for (BllGoodVoiceVO dl : list) {

                //根据地区编号和分值构造排序的缓存结构
                String cacheCityKey = "area_" + areaId;
                String key = "stu_" + dl.getId();
                //如果是加载全国的时候那么构造底层缓存HASH结构
               /* if (areaId == 0) {
*/
                    if (cacheManagerService.exists(key)) {
                        int count = Integer.parseInt(cacheManagerService.hget(key, "count"));
                        cacheManagerService.zadd(cacheCityKey, count, key);
                        continue;
                    }

                    Map<String, String> mp = new HashMap<String, String>();
                    mp.put("id", dl.getId().toString());
                    mp.put("user_name", dl.getUserName());
                    mp.put("school_name", dl.getSchoolName());
                    mp.put("user_url", dl.getUserUrl());
                    mp.put("count", dl.getCount().toString());
                    mp.put("area_id", dl.getAreaId().toString());

                    //缓存的创建时间戳,以后用来每隔5分钟刷新一下用户的投票数量
                    Date date = new Date();
                    DateFormat df = DateFormat.getDateTimeInstance();//可以精确到时分秒
                    String nowString = df.format(date);
                    mp.put("createtime", nowString);
                    cacheManagerService.addHashMutil(key, mp);
                //}

                long index = cacheManagerService.zrevrank(cacheCityKey,key);
                if(index < 0) {
                    cacheManagerService.zadd(cacheCityKey, dl.getCount(), key);
                }
            }

            return true;
        } catch (Exception err) {
            return false;
        }

    }


    /**
     * 根据开始和结束编号获取相关地区的缓存信息
     *
     * @list 选手信息列表
     * @areaId 地区编号
     */
    public List<BllGoodVoiceVO> getListCache(int areaId, int start, int end) {

        String key = "area_" + areaId;

        //获取相关范围的排序的缓存记录
        Set<String> setList = cacheManagerService.getZSetDesc(key, start, end);
        if (setList == null || setList.size() <= 0) return null;

        List<BllGoodVoiceVO> list = new ArrayList<BllGoodVoiceVO>();
        //获取具体的学生信息缓存然后转为VO
        for (String set : setList) {
            Map<String, String> map = cacheManagerService.hGetAll(set);

            BllGoodVoiceVO vo = mapToInfo(map);
            list.add(vo);
        }
        return list;
    }

    /**
     * 添加一个选手的信息进入缓存
     *
     * @dl 选手信息
     */
    public void addUserCache(BllGoodVoiceVO dl) {
        if (dl == null) return;
        String key = "stu_" + dl.getId().toString();
        //如果是加载全国的时候那么构造底层缓存HASH结构

        if (cacheManagerService.exists(key)) return;

        Map<String, String> mp = new HashMap<String, String>();
        mp.put("id", dl.getId().toString());
        mp.put("user_name", dl.getUserName());
        mp.put("school_name", dl.getSchoolName());
        mp.put("user_url", dl.getUserUrl());
        mp.put("count", dl.getCount().toString());
        mp.put("area_id", dl.getAreaId().toString());

        //缓存的创建时间戳,以后用来每隔5分钟刷新一下用户的投票数量
        Date date = new Date();
        DateFormat df = DateFormat.getDateTimeInstance();//可以精确到时分秒
        String nowString = df.format(date);
        mp.put("createtime", nowString);
        cacheManagerService.addHashMutil(key, mp);

    }

    @Override
    public Page getCandidatesList(Page page, String uid) throws ParseException {
        int pageSize = page.getPageSize();
        Integer areaId = Integer.valueOf(page.getParams().get("areaId").toString());
        //开始位置
        int start = (Integer.valueOf(page.getPageNo()) - 1) * Integer.valueOf(page.getPageSize());
        //结束位置
        int end = start + Integer.valueOf(page.getPageSize()) - 1;

        //获取第一条数据,第一条数据为空,刷新缓存
        List<BllGoodVoiceVO> firstVO = getListCache(areaId, 0, 0);
        if (ObjectUtils.isEmpty(firstVO)) {
            synchronized (this) {
                firstVO = getListCache(areaId, 0, 0);
                if (ObjectUtils.isEmpty(firstVO)) {
                    //如果为空(第一次),则从库中捞数据,并放入缓存
                    logger.info("缓存中无任何数据,去库中获取数据并放入缓存|getCandidatesList");
                    flushCache(areaId);
                    if (areaId != 0) {
                        flushCache(0);
                    }
                }
            }
        }
        //获取目标页数据,如果为空,去库查询数据返回,同时刷新缓存
        List<BllGoodVoiceVO> candidatesVO = getListCache(areaId, start, end);
        if (ObjectUtils.isNotNull(candidatesVO) && candidatesVO.size() != 0 && candidatesVO.size() != 4) {
            flushCache(areaId);
            if (areaId != 0) {
                flushCache(0);
            }
        }
        if (ObjectUtils.isEmpty(candidatesVO)) {
            synchronized (this) {
                //加锁后,从缓存中捞一次数据
                candidatesVO = getListCache(areaId, start, end);
                if (ObjectUtils.isEmpty(candidatesVO)) {
                    //从库中捞对应数据,并放入缓存
                    logger.info("缓存中无对应页数据,去库中获取数据|getCandidatesList");
                    List<BllGoodVoicePO> candidatesPO = bllGoodVoiceUserMapper.selectByAreaId(page);
                    candidatesVO = CopyVoPo.copyList(candidatesPO, BllGoodVoiceVO.class);
                    flushCache(areaId);
                    if (areaId != 0) {
                        flushCache(0);
                    }
                }
            }
        }
        candidatesVO = setUserSendState(candidatesVO, uid);
        page.setResults(candidatesVO);
        return page;
    }

    /**
     * 从缓存中获取选手的信息
     */
    public BllGoodVoiceVO getDetailInfoCache(Integer id) {
        String key = "stu_" + id.toString();

        Map<String, String> map = cacheManagerService.hGetAll(key);
        if (map == null) return null;

        BllGoodVoiceVO vo = mapToInfo(map);
        return vo;
    }

    /**
     * mp信息转为VO
     *
     * @list 选手信息列表
     * @areaId 地区编号
     */
    private BllGoodVoiceVO mapToInfo(Map<String, String> mp) {

        if (mp == null || mp.size() <= 0) return null;

        BllGoodVoiceVO vo = new BllGoodVoiceVO();
        if (mp.get("id") != null) {
            vo.setId(Integer.parseInt(mp.get("id")));
        }
        if (mp.get("user_name") != null) {
            vo.setUserName(mp.get("user_name"));
        }
        if (mp.get("school_name") != null) {
            vo.setSchoolName(mp.get("school_name"));
        }
        if (mp.get("user_url") != null) {
            vo.setUserUrl(mp.get("user_url"));
        }
        if (mp.get("count") != null) {
            vo.setCount(Integer.parseInt(mp.get("count")));
        }
        if (mp.get("area_id") != null) {
            vo.setAreaId(Integer.parseInt(mp.get("area_id")));
        }
        return vo;
    }

    /**
     * 好声音活动-根据用户id查询用户详情
     *
     * @param id
     * @return
     */
    public BllGoodVoiceVO selectByPrimaryKey(Integer id, String uid) throws Exception {

        //从缓存中获取用户信息
        BllGoodVoiceVO cacheUserInfo = getDetailInfoCache(id);
        boolean isSend = isSendForUser(id, uid);

        //如果缓存中没有用户信息
        if (ObjectUtils.isEmpty(cacheUserInfo)) {
            synchronized (this) {
                cacheUserInfo = getDetailInfoCache(id);
                if (ObjectUtils.isEmpty(cacheUserInfo)) {
                    logger.info("从redis中获取用户信息失败,尝试从数据库中获取用户信息");
                    BllGoodVoiceVO bllGoodVoiceVO = new BllGoodVoiceVO();
                    BllGoodVoicePO bllGoodVoicePO = bllGoodVoiceUserMapper.selectByPrimaryKey(id);
                    //如果数据库中没有用户信息
                    if (ObjectUtils.isEmpty(bllGoodVoicePO)) {
                        logger.info("好声音活动-根据用户id查询用户详情为空");
                        return null;
                    }
                    CopyVoPo.beanUtilsBean.copyProperties(bllGoodVoiceVO, bllGoodVoicePO);
                    logger.info("好声音活动-根据用户id查询用户详情|BllGoodVoiceUserService|selectByPrimaryKey|bllGoodVoicePO={}", JSON.toJSONString(bllGoodVoicePO));
                    addUserCache(bllGoodVoiceVO);
                    logger.info("好声音活动-将用户信息存入redis中|BllGoodVoiceUserService|selectByPrimaryKey|用户:id={},bllGoodVoiceVO={}", id, bllGoodVoiceVO);
                    bllGoodVoiceVO.setIfVote(isSend);
                    return bllGoodVoiceVO;
                }
            }
        }

        logger.info("好声音活动-尝试从redis中获取的用户信息={}", cacheUserInfo);
        cacheUserInfo.setIfVote(isSend);
        return cacheUserInfo;
    }


    /**
     * 两个日期相差的秒数
     *
     * @list 选手信息列表
     * @areaId 地区编号
     */
    public int calLastedTime(Date startDate, Date endDate) {
        long a = endDate.getTime();
        long b = startDate.getTime();

        int c = (int) ((a - b) / 1000);
        return c;
    }

    private List<BllGoodVoiceVO> flushCache(Integer areaId) {
        List<BllGoodVoicePO> candidatesPO = bllGoodVoiceUserMapper.selectAllByAreaId(areaId);
        List<BllGoodVoiceVO> firstVO = CopyVoPo.copyList(candidatesPO, BllGoodVoiceVO.class);
        setListCache(firstVO, areaId);
        return firstVO;
    }

    /**
     * 查询今天一个用户投选手的票数
     *
     * @param uid
     * @return
     */
    public List<Integer> getStuNumByUserId(String uid) {
        List<Integer> isVoted = bllGoodVoiceNoteMapper.selectNumByUserId(uid);
        logger.info("好声音活动-查询今天一个用户投票选手|BllGoodVoiceUserService|getStuNumByUserId|用户:id={}", uid);
        return isVoted;
    }


    /**
     * 判断能否往下执行
     *
     * @list 选手信息列表
     * @areaId 地区编号
     */
    public boolean isRunNext() {
        String key = "sendApi";
        int current = (int) cacheManagerService.incr(key);
        if (current + 1 > 30000) //如果超出限流大小
            return false;
        else if (current == 1) //只有第一次访问需要设置2秒的过期时间
            cacheManagerService.expire(key, 2);
        return true;
    }

    public boolean ifBeforeDate(String deadLineStr) throws ParseException {
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date deadLine = dateFormat.parse(deadLineStr);
//        Date nowDate = dateFormat.parse("2016-11-21");
        String nowDateStr = dateFormat.format(new Date());
        Date nowDate = dateFormat.parse(nowDateStr);
        return DateUtil.compareDate(deadLine, nowDate);
    }

    /**
     * 埋点
     *
     * @param type
     * @return
     */
    @Override
    public void scanTimes(String type) throws Exception {

        logger.info("好声音活动-埋点|BllGoodVoiceUserService|scanTimes|scanTimes:type={}", type);
        dealTimesToCache(type);

    }


    /**
     * 扫码次数
     */
    public void scanTimes() {
        dealTimesToCache("scan_times");
    }

    /**
     * 扫码用户数
     */
    public void scanUserTimes() {
        dealTimesToCache("user_times");
    }

    /**
     * 处理缓存数据
     *
     * @param key
     */
    private void dealTimesToCache(String key) {
        try {
            DateFormat df2 = DateFormat.getDateTimeInstance();
            Date now = new Date();
            boolean times = cacheManagerService.exists(ACTIVITYTIMES);
            if (times) {//缓存是否存在,是
                //时间是否到同步时间
                String createTime = cacheManagerService.hget(ACTIVITYTIMES, "createTime");
                int second = calLastedTime(df2.parse(createTime), now);
                if (second > TimeUnit.MINUTES.toSeconds(15)) {
                    synchronized (this) {
                        if (second > TimeUnit.MINUTES.toSeconds(15)) {
                            putToCache(key);
                            //同步入库
                            synchronized (this) {
                                BllActivityTimesPO param = new BllActivityTimesPO();
                                BllActivityTimesPO bllActivityTimesPO = bllActivityTimesMapper.selectByPrimaryKey(param);
                                Map<String, String> map = cacheManagerService.hGetAll(ACTIVITYTIMES);
                                BllActivityTimesPO result = insertTOPo(map, bllActivityTimesPO);
                                if (bllActivityTimesPO == null) {
                                    bllActivityTimesMapper.insertSelective(result);
                                } else {
                                    bllActivityTimesMapper.updateByPrimaryKeySelective(result);
                                }
                                //重新设置时间
                                cacheManagerService.hset(ACTIVITYTIMES, "createTime", df2.format(now));
                            }
                        }
                    }
                } else {
                    putToCache(key);
                }
            } else {//不存在放入缓存
                putToCache(key);
                cacheManagerService.hset(ACTIVITYTIMES, "createTime", df2.format(now));
            }
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

    }

    /**
     * 对象转换
     *
     * @param map
     * @param param
     * @return
     */
    private BllActivityTimesPO insertTOPo(Map<String, String> map, BllActivityTimesPO param) {
        if (ObjectUtils.isNull(param)){
            param = new BllActivityTimesPO();
        }
        for (String key : map.keySet()) {
            if (null!=key&&key.equals("user_times")) {
                String count = cacheManagerService.hget(ACTIVITYTIMES, key);
                param.setUserTimes(Integer.parseInt(count));
            }
            if (null!=key&&key.equals("scan_times")) {
                String count = cacheManagerService.hget(ACTIVITYTIMES, key);
                param.setScanTimes(Integer.parseInt(count));
            }
            if (null!=key&&key.equals("banner_times")) {
                String count = cacheManagerService.hget(ACTIVITYTIMES, key);
                param.setBannerTimes(Integer.parseInt(count));
            }
            if (null!=key&&key.equals("activity_times")) {
                String count = cacheManagerService.hget(ACTIVITYTIMES, key);
                param.setActivityTimes(Integer.parseInt(count));
            }
            if (null!=key&&key.equals("share_times")) {
                String count = cacheManagerService.hget(ACTIVITYTIMES, key);
                param.setShareTimes(Integer.parseInt(count));
            }
            if (null!=key&&key.equals("activ_share_times")) {
                String count = cacheManagerService.hget(ACTIVITYTIMES, key);
                param.setActivShareTimes(Integer.parseInt(count));
            }
            if (null!=key&&key.equals("user_share_times")) {
                String count = cacheManagerService.hget(ACTIVITYTIMES, key);
                param.setUserShareTimes(Integer.parseInt(count));
            }
            param.setUpdateTime(new Date());
        }
        return param;
    }

    /**
     * 放入缓存
     *
     * @param key
     */
    private void putToCache(String key) {
        String mapKey = cacheManagerService.hget(ACTIVITYTIMES, key);
        if (mapKey == null) {
            cacheManagerService.hset(ACTIVITYTIMES, key, "1");
        } else {
            cacheManagerService.hincrby(ACTIVITYTIMES, key, 1);
        }
    }


}
